package com.example.demo.annotation;

import com.example.demo.context.TxTransactionConstants;
import com.example.demo.context.TxTransactionContext;
import com.example.demo.listen.TxTransactionListen;
import com.example.demo.util.ConnectionProxy;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.UUID;

import static com.example.demo.context.TxTransactionConstants.TX_REQUEST_HEADER;

/**
 * create by yuaneg on 2018/12/13
 */
@Slf4j
@Aspect
@Component
public class TxTransactionAspect {

    @Autowired
    private TxTransactionListen txTransactionListen;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Order(1)
    @Around("@annotation(txTransaction)")
    public Object txTransaction(ProceedingJoinPoint point, TxTransaction txTransaction) throws Throwable {
        if (txTransaction.master()) {
            String transactionId = UUID.randomUUID().toString();
            log.info("事物ID:" + transactionId);
            TxTransactionContext.setTransactionId(transactionId);
            Object object;
            try {
                object = point.proceed();
            } catch (Throwable throwable) {
                stringRedisTemplate.convertAndSend(TxTransactionConstants.TX_NOTIFY_CHAT_FAIL, transactionId);
                throw throwable;
            }
            stringRedisTemplate.convertAndSend(TxTransactionConstants.TX_NOTIFY_CHAT_SUCCESS, transactionId);
            return object;
        }
        return point.proceed();
    }


    @Order(2)
    @Around("execution(* javax.sql.DataSource.getConnection(..))")
    public Object connectionAround(ProceedingJoinPoint point) throws Throwable {
        Object object = point.proceed();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null != attributes) {
            HttpServletRequest request = attributes.getRequest();
            if (null != request) {
                String transactionId = request.getHeader(TX_REQUEST_HEADER);
                if (null != transactionId) {
                    ConnectionProxy invocationHandler = new ConnectionProxy((Connection) object, txTransactionListen, transactionId);
                    Object newProxyInstance = Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[]{Connection.class}, invocationHandler);
                    Connection connection = (Connection) newProxyInstance;
                    connection.setAutoCommit(false);
                    return connection;
                }
            }
        }
        return object;
    }
}
